/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Communication;

import Request.RequestRdyArg;
import Request.RequestRdyUtil;
import Events.*;
import Response.*;
import Request.RequestEndListener;
import Request.RequestEndEvent;
import Request.RequestResultReadyEvent;
import Request.RequestReadyHandler;
import java.io.*;
import java.util.*;
import java.net.*;
import javax.swing.event.EventListenerList;

/**
 *
 * @author lcy
 */

public class ServerTransceiver extends Communicator implements RequestEndListener, 
        RequestReadyHandler, ProbeRespHandler {

    private ServerSocket _server_sock;
    private int _serv_port;
    
    private List<RequestThread> _client_request_list;

    //private List<RequestReadyHandler> _r_rdy_handlers;
    //private NetDataResponse _response;

    private ProbeSockThread _probe_thread;

    private EventListenerList _probeEventListeners;

    private EventListenerList _probeRespListeners;

    private EventListenerList _sendRespListeners;

    private RequestRdyUtil _request_rdy_util;
    
    public ServerTransceiver(){
        int _server_port = 9000;
	try{
	    
	    _server_sock = new ServerSocket(_server_port);
	    _debugger.LogInfo("ServerTransceiver starts at port " + _server_port);
	}catch(IOException ex){
	    _debugger.LogInfo("Could not listen on port: " + _server_port);
	    System.exit(-1);
	}
    }

    public ServerTransceiver(int port){
	try{
	    _server_sock = new ServerSocket(port); 
	}catch(IOException ex){
	    _debugger.LogInfo("Could not listen on port: " + port);
	    System.exit(-1);
	}
    }

    //The rest of the common part of all Constructors.
    {
        _client_request_list = new LinkedList<RequestThread>();
        //_r_rdy_handlers = new LinkedList<RequestReadyHandler>();
        //_response = null;
        _probeEventListeners = new EventListenerList();
        _probe_thread = new ProbeSockThread(9010);
        _probeRespListeners = new EventListenerList();
        _sendRespListeners = new EventListenerList();
        _request_rdy_util = new RequestRdyUtil();
    }

    /*****************************************************
     * Function:
     * Summary:
     * Input:
     * Returns:
     * Side effects:
     * Pre-Conditions:
    ******************************************************/
    public void OpenTransceiver(){

        //Register: Inform the thread to send probe message
        addProbeEventHandler(_probe_thread);

        //Register to probe_thread to listen for any response from client;
        
        _probe_thread.addProbeRespHandler(this);
        
        _probe_thread.start();


        /*
        try{
            _probe_thread.join();
        }catch(InterruptedException ex){
            ex.printStackTrace();
            //Then do nothing;
        }
         */
        
    }


    /*****************************************************
     * Function:
     * Summary:
     * Input:
     * Returns:
     * Side effects:
     * Pre-Conditions:
    ******************************************************/
    public void CloseTransceiver(){
        try{
            //Remove all handlers
            removeProbeEventHandler(_probe_thread);
            _probe_thread.removeProbeRespHandler(this);
            
            _server_sock.close();
        }catch (IOException ex){
            _debugger.LogInfo("Could not close server port: ");
	    System.exit(-1);
        }
        
    }

    /*****************************************************
     * Function:
     * Summary:
     * Input:
     * Returns:
     * Side effects:
     * Pre-Conditions:
    ******************************************************/

    public Socket ListenForRequest() throws SocketTimeoutException{

        InetSocketAddress new_request = null;
        Socket client_sock = null;
        
        try{

           _debugger.LogInfo("Server Transceiver: waiting for call...");
	   client_sock = _server_sock.accept();

        }catch(SocketTimeoutException ex2){
            throw ex2;
        }catch(IOException ex1){
            _debugger.LogInfo("Server Transceiver: IO error occurs during accept");
        }

        return client_sock;
    }
    
    /*****************************************************
     * Function:
     * Summary:
     * Input:
     * Returns:
     * Side effects:
     * Pre-Conditions:
    ******************************************************/
    public void ProcessNewRequest(Socket new_request){

        /*Start a new thread*/
        
        if(_client_request_list != null){
            RequestThread new_entry = new RequestThread(new_request);
            _client_request_list.add(new_entry);
         
            //Register all relevant events
            this.addSendRespHandler(new_entry);
            new_entry.addRequestEndListener(this);
            new_entry.addRequestReadyHandler(this);
            new_entry.start();
        }
        else{
            _debugger.LogInfo("ServerTransceiver: client list null");
        }
    }

    
    //Remove the thread from the transceiver's list
    //TODO de-register all events associated with the game server;
    public synchronized void EndingRequest(RequestEndEvent event){
        
        Iterator iter = _client_request_list.iterator();

        RequestThread rq_thd;
        while(iter.hasNext()){

            rq_thd = (RequestThread)(iter.next());
            Socket eve_request = event.GetClientAddr();

            if(eve_request == rq_thd.GetClientAddr()){
                this.removeSendRespHandler(rq_thd);
                iter.remove();
                break;
            }
        }
        
    }


    public void AddValidateUserMsg(NetDataResponse response, UserValidResult result){

        try{
           String msg = "";
           
           switch(result){
               case VALID:
                   msg = ResponseMsgList.msg_list[ResponseMsgList.VALID_IDX];
                   break;
               case INVALID:
                   msg = ResponseMsgList.msg_list[ResponseMsgList.INVALID_IDX];
                   break;
               case EXIST:
                   msg = ResponseMsgList.msg_list[ResponseMsgList.EXISTED_IDX];;
                   break;
               case NON_EXIST:
                   msg = ResponseMsgList.msg_list[ResponseMsgList.NONEXIST_IDX];
                   break;
               case PASSWD_MISMATCH:
                   msg =  ResponseMsgList.msg_list[ResponseMsgList.NOMATCH_IDX];
                   break;
           }

           response.AppendResultMsg(msg);
           //channel.writeObject(response);
           //channel.flush();
           //out_stream.writeBytes(msg);

        }catch(Exception ex){
            //ex.printStackTrace();
            _debugger.LogInfo("Server Transceiver: out channel not available,"
                    + "client closes connection?");
        }
    }

    public void HandleProbeResp(ProbeRespReceived eve){
        fireProbeRespEvent(eve);
    }

    //Handle tcp request ready event
    public synchronized void HandleResponse(RequestResultReadyEvent event){
        RequestRdyArg arg = event.GetArg();

        _request_rdy_util._fireRequestReadyEvent(arg);
    }

    
    public synchronized void addRequestReadyHandler( RequestReadyHandler l ) {
        _request_rdy_util.addRequestReadyHandler(l);
    }

    public synchronized void removeRequestReadyHandler( RequestReadyHandler l ) {
        _request_rdy_util.removeRequestReadyHandler(l);
    }

    /*
    public synchronized void _fireRequestReadyEvent(RequestRdyArg arg) {

        RequestResultReadyEvent request_ready =
                new RequestResultReadyEvent(this,arg);

        Iterator listeners = _r_rdy_handlers.iterator();

        while(listeners != null && listeners.hasNext() ) {
            RequestReadyHandler handler = ((RequestReadyHandler) listeners.next());
            handler.HandleResponse(request_ready);
            _debugger.LogInfo("Server Transceiver: request ready handler executed...");
        }
    }
    */

    public synchronized void addProbeEventHandler(ProbeEventHandler l){
        _probeEventListeners.add(ProbeEventHandler.class, l);
    }

    public synchronized void removeProbeEventHandler(ProbeEventHandler l){
         _probeEventListeners.remove(ProbeEventHandler.class, l);
    }

    // Adapted from http://www.exampledepot.com/egs/java.util/custevent.html
    public synchronized void fireProbeSocketEvent(ProbeEvent eve){

        //TODO: analyse arg if we need in the future;
        Object[] listeners = _probeEventListeners.getListenerList();

        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==ProbeEventHandler.class) {
                ((ProbeEventHandler)listeners[i+1]).SocketProbeOccurred(eve);
            }
        }

    }

    public synchronized void addProbeRespHandler(ProbeRespHandler l){
        _probeRespListeners.add(ProbeRespHandler.class, l);
    }

    public synchronized void removeProbeRespHandler(ProbeRespHandler l){
       _probeRespListeners.remove(ProbeRespHandler.class, l);
    }

    public synchronized void fireProbeRespEvent(ProbeRespReceived eve){
         //TODO: analyse arg if we need in the future;
        Object[] listeners = _probeRespListeners.getListenerList();

        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==ProbeRespHandler.class) {
                ((ProbeRespHandler)listeners[i+1]).HandleProbeResp(eve);
            }
        }
    }

     public synchronized void addSendRespHandler(SendRespHandler l){
        _sendRespListeners.add(SendRespHandler.class, l);
    }

    public synchronized void removeSendRespHandler(SendRespHandler l){
       _sendRespListeners.remove(SendRespHandler.class, l);
    }

    public synchronized void fireSendRespEvent(SendRespEvent eve){
         //TODO: analyse arg if we need in the future;
        Object[] listeners = _sendRespListeners.getListenerList();

        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==SendRespHandler.class) {
                ((SendRespHandler)listeners[i+1]).RespSendHandler(eve);
            }
        }
    }
}


